//忽略文件缓存
//单指内存和redis的连接内存
package illusion

import (
	"sync"
	"strings"
	"strconv"
	"time"
	"github.com/go-redis/redis"
)

//type defaultValueFunc func()string

//作为一个cache应该有哪些接口呢
type Cache interface {
	Has(string) bool
	Get(string) string
	//DefaultFuncGet(string, defaultValueFunc)string
	DefaultGet(string,string) string
	Set(string, string)
	//ExpireSet(string, string, int)//int为过期时间
	Flush() //清空！！
	Forget(string)
}

type cacheFactory func(int)Cache
//这就是你的不对了
type Cacher struct {
	factory  cacheFactory
	duration int
	err error
}
var cr *Cacher
var crOnce sync.Once
func getCacher() *Cacher {
	crOnce.Do(func(){
		cr = &Cacher{
			factory: nil,
			duration: 0,
			err: nil,
		}
	})
	return cr
}
func (c *Cacher)SetCache(_type string,_duration int)  {
	//if c.factory == nil {
		//panic("cache的工厂函数为nil")
	//}
	if _duration <= 0 {
		c.duration = 0 //意味forever
	}else {
		c.duration = _duration
	}
	switch _type {
	case CacheOfMemory:
		c.factory = cacheFactoryOfMemory
		break
	case CacheOfRedis:
		c.factory = cacheFactoryOfRedis
	//	break
	default:
		panic("未知的cache存储类型:" + _type)
	}
	//return c.factory(c.duration)
}
func (c *Cacher)Cache() Cache {
	if c.factory == nil {
		panic("缓存工厂函数为nil")
	}
	return c.factory(c.duration)
}


type MemoryCache struct {
	sync.RWMutex //写锁，读不锁
	cache map[string]string
	duration int
}
var mc *MemoryCache
var mcOnce sync.Once

func cacheFactoryOfMemory(_duration int) Cache{
	mcOnce.Do(func(){
		mc = &MemoryCache{
			cache: make(map[string]string),
			duration: _duration,
		}
	})
	return mc
}
func (mc *MemoryCache)isValueExpired(value string) bool{
	if mc.duration == 0 { //0就是永远不会过期
		return false
	}
	arr := strings.Split(value, "&")
	onceString := arr[len(arr)-1]
	once,err := strconv.ParseInt(onceString, 10, 64)
	if err != nil {
		panic(err.Error())
	}
	if time.Now().Unix() > once { //过期了
		return true
	}
	return false
}

func (mc *MemoryCache)rawValue(origValue string) string{
	arr := strings.Split(origValue, "&")
	return strings.Join(arr[:len(arr)-1], "&") //放空最后一个
}

func (mc *MemoryCache)getMixedValue(value string) string{
	if mc.duration == 0 { //
		return value+"&0"
	}
	expiredTimestamp := int64(mc.duration) + time.Now().Unix()
	extra := strconv.FormatInt(expiredTimestamp, 10) //64位数字转换
	return value + "&" + extra
}


func (mc *MemoryCache)Has(key string) bool {
	if _, ok := mc.cache[key]; ok {
		return true
	} else {
		return false
	}
}


func (mc *MemoryCache)Get(key string) string {
	if mc.Has(key) {
		mixedValue := mc.cache[key]
		logging("获取到混合值=>" + key + ":" + mixedValue)
		if mc.isValueExpired(mixedValue) {
			logging(key + "键值过期了")
			return ""
		}
		return mc.rawValue(mixedValue)
	}
	return ""
}

/*func (mc *MemoryCache)DefaultFuncGet(key string, dvf defaultValueFunc) string {
	mc.Lock()
	defer mc.Unlock()
	value := mc.Get(key)
	if value == "" { //不存在
		rawValue := dvf()
		mixedValue := mc.getMixedValue(rawValue)
		mc.cache[key] = mixedValue
		return rawValue
	}
	return value
    if mc.Has(key) {
		mixedValue := mc.cache[key]
		if mc.isValueExpired(mixedValue) { //

		}
		//未过期
		return mc.rawValue(mixedValue)
	}
	if dvf == nil {
		panic("defaultValueFunc参数不能为空")
	}
	mixedValue := mc.getMixedValue(dvf())
	mc.cache[key] = mixedValue
	return mixedValue
}*/

func (mc *MemoryCache)DefaultGet(key string, defaultValue string) string{
	value := mc.Get(key)
	if value == "" {
		return defaultValue
	}
	//返回默认值就好
	return value
	/*if mc.Has(key) {
		value := mc.cache[key]
		if mc.isValueExpired(value) {
			return defaultValue
		}
		rawValue := mc.rawValue(value)
		return rawValue
	}

	return defaultValue*/
}

//如果原有值，会被覆盖
func (mc *MemoryCache)Set(key string, value string) {
//	if value == "" { //这个就不设置了???
		//return
	//}
	mc.Lock()
	defer mc.Unlock()
	mixedValue := mc.getMixedValue(value)
	logging("设置缓存=>" + key + ":" + mixedValue)
	mc.cache[key] = mixedValue
}

//clear mc.cache
func (mc *MemoryCache)Flush() {
	mc.Lock()
	defer mc.Unlock()
	for key := range mc.cache {
		delete(mc.cache, key)
	}
}

func (mc *MemoryCache)Forget(key string) {
	mc.Lock()
	defer mc.Unlock()
	delete(mc.cache, key)
}

type RedisCache struct {
	sync.RWMutex //写锁，读不锁
	client *redis.Client
	duration int
}
var redisCache *RedisCache
var redisOnce sync.Once

func cacheFactoryOfRedis(_duration int) Cache {
	redisOnce.Do(func(){
		redisCache = &RedisCache{
			client: nil,
			duration: _duration, //单打green
		}
	})
	//redisCache.ensureRedisConnect()
	return redisCache
}

//确保redis的超时问题
func (rc *RedisCache)ensureRedisConnect(){
	if rc.client == nil {
		rc.client = cacheRedisClient()
	}
	if _, err := rc.client.Ping().Result(); err != nil { //出错
		rc.client = cacheRedisClient()
		if _,err = rc.client.Ping().Result(); err != nil {
			panic(err.Error())
		}
	}
	return
}

func (rc *RedisCache)Has(key string) bool{
	rc.ensureRedisConnect()
	cmd := rc.client.Expire(key, time.Duration(rc.duration)*time.Second)
	ok,err := cmd.Result()
	if ok && err == nil {
		return true
	}
	if err != nil {
		logging(err.Error())
	}
	return false
}

func (rc *RedisCache)Get(key string)string {
	//if rc.Has(key) {
	rc.ensureRedisConnect()
		cmd := rc.client.Get(key)
		val, err := cmd.Result()
		if err != nil {
			logging(err.Error())
			return ""
		}
		return val
	//}
	//return ""
}

/*func (rc *RedisCache)DefaultFuncGet(key string, dvf defaultValueFunc) string {
	rc.Lock()
	defer rc.Unlock()
	if rc.Has(key) {
		val := rc.Get(key)
		if val == "" {
			newValue := dvf()
			rc.client.Set(key, newValue, time.Duration(rc.duration) * time.Second)
			return newValue
		}
		return val
	}
	newValue := dvf()
	rc.client.Set(key, newValue, time.Duration(rc.duration) * time.Second)
	return newValue
}*/

func (rc *RedisCache)DefaultGet(key string,defaultValue string)string {
	rc.ensureRedisConnect()
	val := rc.Get(key)
	if val == "" {
		return defaultValue
	}
	return val
}

func (rc *RedisCache)Set(key string, value string) {
	rc.Lock()
	defer rc.Unlock()
	rc.ensureRedisConnect()
	rc.client.Set(key, value, time.Duration(rc.duration) * time.Second)
}

func (rc *RedisCache)Flush() {
	rc.Lock()
	defer rc.Unlock()
	rc.ensureRedisConnect()
	rc.client.FlushDb()
}

func (rc *RedisCache)Forget(key string) {
	rc.Lock()
	defer rc.Unlock()
	rc.ensureRedisConnect()
	rc.client.Del(key)
}